/*
 * Volcado.c
 *
 *  Created on: 28/08/2012
 */
#include "Volcado.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VERSION "1.0"
#define CERO 48

/*
 * Pre: Los parametros argc y argv respetan la convecion especificada en la
 * funcion "volcado".
 *
 * Post: Se obtienen las especificaciones para el volcado de los parametros
 * arc y argv.
 */
int procesarArgumentos(int argc , char *argv[] , char *modo ,
				char *operacion , char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de ayuda de la aplicación.
 */
void printHelp();

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de la version de la aplicación.
 */
void printVersion();

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado.
 * Post: Se ha realizado el volcado.
 */
int volcar( char *modo , char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado a binario.
 * Post: Se ha realizado el volcado a binario.
 */
int volcarABinario(char ** delimitador, int *longitud ,
					FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado a octal.
 * Post: Se ha realizado el volcado a octal.
 */
int volcarAOctal(char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el formateo de la entrada.
 * Post: Se ha realizado el formateo de la entrada.
 */
int formatear(char ** delimitador, int *longitud , FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre: Se respeta la siguiente convencion de argumentos:
 *
 * Usage:

 tp0 -h\n	tp0 -V\n	tp0 [options]

 Options:
	-h, --help	Print this information.
	-V, --version	Print version and quit.
	-O, --octal	Octal dump mode: encode the input (binary file) in octal
								format (default).
	-b, --binary	Binary mode: decode the input (octal dump file) in binary
								format.
	-i, --input	Path to input file (defaults to stdin).
	-o, --output	Path to output file (defaults to stdout).
	-l, --length	Number of octal bytes per line (line length) at the
					output in octal dump mode (0 = default = no limit).
	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only
					a single byte. Used as delimiter for octal output and input\n");
					files (default: none).
	Examples:
		tp0\n	tp0 -i -
		tp0 -i - -o - -O
		tp0 -b
		tp0 -i /dev/random -o /tmp/random-octaldump -l 12

 * Post: Se realiza el volcado de acuerdo a los argumentos.
 */
int volcado(int argc , char *argv[]) {
	int retorno;
	char modo; // b , o
	char operacion; // v [volcado] , h [h] , V [version]
	char *delimitador; // cadena a
	int longitud;
	FILE* fdEntrada;
	FILE* fdSalida;
	//valores por defecto
	fdEntrada = stdin;
	fdSalida = stdout;
	longitud = 0;
	modo = 'O';
	operacion = 'v';
	delimitador = "";
	retorno = procesarArgumentos(argc , argv , &modo , &operacion , &delimitador, &longitud ,
								&fdEntrada , &fdSalida);
	if (retorno)
		fprintf(stderr,"ERROR\n");
	else {
		switch (operacion) {
			//help
			case 'h' :	printHelp();
						break;
			//version
			case 'V' :	printVersion();
						break;
			//volcado
			case 'v' :	retorno = volcar(&modo ,&delimitador, &longitud , &fdEntrada , &fdSalida);
						break;
			//formatear
			case 'f' :  retorno = formatear(&delimitador, &longitud , &fdEntrada , &fdSalida);
						break;
		}
	}
	if (fdEntrada != NULL) {
		fclose(fdEntrada);
	}
	if (fdSalida != NULL) {
		fclose(fdSalida);
	}

	return retorno;
}

/*
 * Pre: Los parametros argc y argv respetan la convecion especificada en la
 * funcion "volcado".
 *
 * Post: Se obtienen las especificaciones para el volcado de los parametros
 * arc y argv.
 */
int procesarArgumentos(int argc , char *argv[] , char *modo , char *operacion , char ** delimitador,
						int *longitud , FILE **fdEntrada , FILE **fdSalida){
	int retorno = 0;
	int numArg;
	numArg = 1;
	//Exploración de Argumentos
	while ( (numArg < argc) && (retorno)) {
		char* arg;
		arg = argv[numArg];
		numArg++;
		if (arg[0] == '-') {
			if(strlen(arg) == 2) {
				switch (arg[1]) {
					//help
					case 'h' :	*operacion = 'h';
								numArg++;
								break;
					//version
					case 'V' :	*operacion = 'V';
								numArg++;
								break;
					//volcado a binario
					case 'b' :	*operacion = 'v';
								*modo = 'b';
								numArg++;
								break;
					//volcado a Octal
					case 'O' :	*operacion = 'v';
								*modo = 'O';
								numArg++;
								break;
					//entrada desde archivo
					case 'i' :
								if (*fdEntrada ==stdin) {
									/*
									arg = argv[numArg];

									if (arg[0] != '-') {
										*fdEntrada = fopen(arg , "r");
										retorno |= !(*fdEntrada);
										if(*fdEntrada == NULL){
											*fdEntrada = stdin;
										}
									}*/
								}
								numArg++;
								break;
					//salida a archivo
					case 'o' :	if(*fdSalida == stdout){
									arg = argv[numArg];
									if (arg[0] != '-') {
										*fdSalida = fopen(arg , "w");
										retorno |= !(*fdSalida);
										if(*fdSalida == NULL){
											*fdSalida = stdout;
										}
									}
								}
								numArg++;
								break;
					//delimitador
					case 'd' :		if (argv[numArg] != NULL)
									*delimitador = argv[numArg];
									numArg++;
									break;
					//longitud
					case 'l' :		*longitud = atoi(argv[numArg]);
									numArg++;
									break;
					//formatear
					case 'f' :		*operacion = 'f';
									numArg++;
									break;
					default	 :		numArg++;
									retorno = 1;
									break;
					}// fin switch
			}//fin control strlen(arg) == 2
			else {
				retorno = 1;
			}
		} else {
			retorno = 1;
		}
	}
	return retorno;
}

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de ayuda de la aplicacion.
 */
void printHelp() {
	printf("Usage:\n	tp0 -h\n	tp0 -V\n	tp0 [options]\n");
	printf("Options:\n");
	printf("	-h, --help	Print this information.\n");
	printf("	-V, --version	Print version and quit.\n");
	printf("	-O, --octal	Octal dump mode: encode the input (binary file) in octal \n");
	printf("			format (default).\n");
	printf("	-b, --binary	Binary mode: decode the input (octal dump file) in binary \n");
	printf("			format.\n");
	printf("	-i, --input	Path to input file (defaults to stdin).\n");
	printf("	-o, --output	Path to output file (defaults to stdout).\n");
	printf("	-l, --length	Number of octal bytes per line (line length) at the \n");
	printf("			output in octal dump mode (0 = default = no limit).\n");
	printf("	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only\n");
	printf("			a single byte. Used as delimiter for octal output and input\n");
	printf("			files (default: none).\n");
	printf("Examples:\n	tp0\n	tp0 -i -\n	tp0 -i - -o - -O\n	tp0 -b\n");
	printf("	tp0 -i /dev/random -o /tmp/random-octaldump -l 12\n");
}

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de la version de la aplicacion.
 */
void printVersion() {
	printf("Version: %s\n",VERSION);
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado.
 * Post: Se ha realizado el volcado.
 */
int volcar( char *modo , char **delimitador , int *longitud ,
				FILE** fdEntrada , FILE** fdSalida) {
	int retorno;
	retorno = 0;
	if ( *modo == 'b' ) {
		volcarABinario(delimitador, longitud , fdEntrada , fdSalida);
	} else{
		volcarAOctal(delimitador, longitud , fdEntrada , fdSalida);
	}

	return retorno;
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado a binario.
 * Post: Se ha realizado el volcado a binario.
 */
int volcarABinario(char ** delimitador, int *longitud ,
					FILE **fdEntrada , FILE **fdSalida){
	char vector[3];
	int numero;
	int contador;
	contador = 1;
	fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
	while (!feof(*fdEntrada)){
		numero = vector[0]*64+vector[1]*8+vector[2] - CERO*73;
		fseek(*fdEntrada , strlen(*delimitador) , SEEK_CUR);
		if (contador == *longitud){
			fseek(*fdEntrada , 1 , SEEK_CUR);
			contador = 0;
		}
		fprintf(*fdSalida,"%c",numero);
		fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
		contador++;
	}
	return 0;
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado a octal.
 * Post: Se ha realizado el volcado a octal.
 */
int volcarAOctal(char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida){
	char vector[3];
	int numero;
	int contador;
	contador = 1;
	numero = fgetc(*fdEntrada);
	while (!feof(*fdEntrada)){
		int i;
		for ( i = 0 ; i<3 ; i++){
			vector[i] = (char) (CERO + (numero % 8));
			numero = numero /8;
		}
		fprintf(*fdSalida,"%c%c%c%s",vector[2],vector[1],vector[0],*delimitador);
		if ((contador == *longitud)&&(!feof(*fdEntrada))){
			fprintf(*fdSalida,"\n");
			contador = 0;
		}
		contador++;
		numero = fgetc(*fdEntrada);
	}
	return 0;
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el formateo de la entrada.
 * Post: Se ha realizado el formateo de la entrada.
 */
int formatear(char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida){
	char vector[3];
	int contador;
	contador = 1;
	fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
	while (!feof(*fdEntrada)){
		fprintf(*fdSalida,"%c%c%c%s",vector[0],vector[1],vector[2],*delimitador);
		if ((contador == *longitud)&&(!feof(*fdEntrada))){
			fprintf(*fdSalida,"\n");
			contador = 0;
		}
		fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
		contador++;
	}
	return 0;
}
